package org.jacp.demo.util;

/*
 Core SWING Advanced Programming 
 By Kim Topley
 ISBN: 0 13 083292 8       
 Publisher: Prentice Hall  
 */

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.File;
import java.io.IOException;

import javax.swing.JPanel;

import org.jacp.demo.globals.GlobalSettings;

public class PanelDropTarget implements DropTargetListener {

    public PanelDropTarget(final JPanel pane) {
	this.pane = pane;

	// Create the DropTarget and register
	// it with the JPanel.
	dropTarget = new DropTarget(pane, DnDConstants.ACTION_COPY_OR_MOVE,
		this, true, null);
    }

    // Implementation of the DropTargetListener interface
    @Override
    public void dragEnter(final DropTargetDragEvent dtde) {
	DnDUtils.debugPrintln("dragEnter, drop action = "
		+ DnDUtils.showActions(dtde.getDropAction()));

	// Get the type of object being transferred and determine
	// whether it is appropriate.
	checkTransferType(dtde);

	// Accept or reject the drag.
	acceptOrRejectDrag(dtde);
    }

    @Override
    public void dragExit(final DropTargetEvent dte) {
	DnDUtils.debugPrintln("DropTarget dragExit");
    }

    @Override
    public void dragOver(final DropTargetDragEvent dtde) {
	DnDUtils.debugPrintln("DropTarget dragOver, drop action = "
		+ DnDUtils.showActions(dtde.getDropAction()));

	// Accept or reject the drag
	acceptOrRejectDrag(dtde);
    }

    @Override
    public void dropActionChanged(final DropTargetDragEvent dtde) {
	DnDUtils.debugPrintln("DropTarget dropActionChanged, drop action = "
		+ DnDUtils.showActions(dtde.getDropAction()));

	// Accept or reject the drag
	acceptOrRejectDrag(dtde);
    }

    @Override
    public void drop(final DropTargetDropEvent dtde) {
	DnDUtils.debugPrintln("DropTarget drop, drop action = "
		+ DnDUtils.showActions(dtde.getDropAction()));

	// Check the drop action
	if ((dtde.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0) {
	    // Accept the drop and get the transfer data
	    dtde.acceptDrop(dtde.getDropAction());
	    final Transferable transferable = dtde.getTransferable();

	    try {
		final boolean result = dropComponent(transferable);

		dtde.dropComplete(result);
		DnDUtils.debugPrintln("Drop completed, success: " + result);
	    } catch (final Exception e) {
		DnDUtils.debugPrintln("Exception while handling drop " + e);
		dtde.dropComplete(false);
	    }
	} else {
	    DnDUtils.debugPrintln("Drop target rejected drop");
	    dtde.rejectDrop();
	}
    }

    // Internal methods start here

    protected boolean acceptOrRejectDrag(final DropTargetDragEvent dtde) {
	final int dropAction = dtde.getDropAction();
	final int sourceActions = dtde.getSourceActions();
	boolean acceptedDrag = false;

	DnDUtils.debugPrintln("\tSource actions are "
		+ DnDUtils.showActions(sourceActions) + ", drop action is "
		+ DnDUtils.showActions(dropAction));

	// Reject if the object being transferred
	// or the operations available are not acceptable.
	if (!acceptableType
		|| (sourceActions & DnDConstants.ACTION_COPY_OR_MOVE) == 0) {
	    DnDUtils.debugPrintln("Drop target rejecting drag");
	    dtde.rejectDrag();

	} else if ((dropAction & DnDConstants.ACTION_COPY_OR_MOVE) == 0) {
	    // Not offering copy or move - suggest a copy
	    DnDUtils.debugPrintln("Drop target offering COPY");
	    dtde.acceptDrag(DnDConstants.ACTION_COPY);
	    acceptedDrag = true;
	} else {
	    // Offering an acceptable operation: accept
	    DnDUtils.debugPrintln("Drop target accepting drag");
	    dtde.acceptDrag(dropAction);
	    acceptedDrag = true;
	}

	return acceptedDrag;
    }

    protected void checkTransferType(final DropTargetDragEvent dtde) {
	// Only accept a flavor that returns a Component
	acceptableType = false;
	final DataFlavor[] fl = dtde.getCurrentDataFlavors();
	for (final DataFlavor element : fl) {
	    final Class dataClass = element.getRepresentationClass();

	    if (dataClass == String.class) {
		targetFlavor = element;
		acceptableType = true;
		break;
	    }
	}

	DnDUtils.debugPrintln("File type acceptable - " + acceptableType);
    }

    protected boolean dropComponent(final Transferable transferable)
	    throws IOException, UnsupportedFlavorException {
	final Object o = transferable.getTransferData(targetFlavor);

	if (o instanceof String) {
	    DnDUtils.debugPrintln("Dragged component class is "
		    + o.getClass().getName());
	    final String filePath = o.toString();
	    final File f = new File(filePath);
	    // TODO restore getPanel
/*	    final DropShadowPanel tmp = DropShadowPanelCreator.getPanel(f,
		    f.getAbsolutePath());
	    pane.add(tmp);*/
	    pane.validate();
	    return true;
	}
	return false;
    }

    protected JPanel pane;

    protected DropTarget dropTarget;

    protected boolean acceptableType; // Indicates whether data is acceptable

    protected DataFlavor targetFlavor; // Flavor to use for transfer
}

class DnDUtils {
    public static String showActions(final int action) {
	String actions = "";
	if ((action & (DnDConstants.ACTION_LINK | DnDConstants.ACTION_COPY_OR_MOVE)) == 0) {
	    return "None";
	}

	if ((action & DnDConstants.ACTION_COPY) != 0) {
	    actions += "Copy ";
	}

	if ((action & DnDConstants.ACTION_MOVE) != 0) {
	    actions += "Move ";
	}

	if ((action & DnDConstants.ACTION_LINK) != 0) {
	    actions += "Link";
	}

	return actions;
    }

    public static boolean isDebugEnabled() {
	return debugEnabled;
    }

    public static void debugPrintln(final String s) {
	if (debugEnabled) {
	    System.out.println(s);
	}
    }

    private static boolean debugEnabled = GlobalSettings.DEBUG_LOG;
}
